Перейти к основному содержимому

5.01. Ext JS

Разработчику Архитектору

Ext JS

Введение

Ext JS — это зрелый JavaScript-фреймворк высокого уровня, предназначенный для разработки масштабных, богатых по функциональности веб-приложений с ориентацией на корпоративный сегмент. Он отличается не просто богатым набором пользовательских интерфейсов, но и продуманной архитектурой, позволяющей строить приложения, сравнимые по сложности, структуре и предсказуемости поведения с настольными приложениями. В отличие от лёгких библиотек вроде jQuery UI или Bootstrap-компонентов, Ext JS решает задачу целостной инфраструктуры для front-end-разработки: от описания данных и их привязки к интерфейсу — до управления жизненным циклом приложения, маршрутизации, локализации, темизации и инструментов сборки.

Разработка на Ext JS подразумевает принятие определённой парадигмы: декларативное описание интерфейса, строгая типизация посредством метаданных, централизованное управление состоянием и событиями, а также единый подход к совместимости и производительности. Это делает его особенно ценным в контексте длительных проектов с большими командами, где важны предсказуемость, сопровождаемость и соответствие корпоративным стандартам.

Несмотря на то, что современный front-end-ландшафт доминируется React, Vue и Angular, Ext JS остаётся востребованным в нишах, где требуется максимальная функциональность «из коробки», строгая поддержка старых браузеров (вплоть до Internet Explorer 11 в более ранних версиях) и готовые решения для сложных задач — таких как редактируемые таблицы с сотнями столбцов, встроенные диаграммы с динамическим обновлением, или формы с многоуровневой валидацией и привязкой к вложенными моделям.


Что такое Ext JS

Ext JS — это полноценный фронтенд-фреймворк с собственным языком описания структур, механизмом наследования, системой событий, встроенными абстракциями для работы с данными и инструментами для разработки и развёртывания. Его ключевая идея — отделение описания интерфейса и логики от реализации визуального представления и взаимодействия. Разработчик описывает «что должно быть», а фреймворк отвечает за «как это сделать» с учётом кроссбраузерности, доступности, производительности и согласованности.

Фреймворк строится на принципах объектно-ориентированного программирования и шаблонов проектирования, адаптированных к среде JavaScript. В Ext JS всё — объект: компонент, контейнер, модель данных, хранилище, прокси-адаптер для передачи данных, событие, даже конфигурация темы. Каждый объект имеет строгий жизненный цикл: инициализация, рендеринг, уничтожение — и сопровождается соответствующими событиями, на которые можно подписаться. Это позволяет выстраивать чёткую иерархию ответственности и избегать «спагетти-кода» даже в очень больших приложениях.

Ext JS не заменяет HTML/CSS/JavaScript, но ограничивает и канализирует их использование: разработчик редко работает напрямую с DOM-элементами, не пишет обработчики onclick, не управляет display: none вручную. Вместо этого он оперирует абстракциями, гарантирующими корректное поведение на всех поддерживаемых платформах.


История развития

Истоки Ext JS восходят к 2006 году, когда Джек Слокам (Jack Slocum), работая в условиях ограниченной выразительности тогдашних веб-технологий, создал расширение для Yahoo! UI Library (YUI), названное YUI-Ext. Цель была проста: добавить в YUI недостающие компоненты — в первую очередь, решётку (grid) с сортировкой, фильтрацией и редактированием, а также улучшенные оконные и панельные элементы. YUI-Ext быстро стал популярным благодаря высокому качеству реализации и вниманию к деталям: плавной анимации, гибкому управлению размерами, интуитивному поведению.

В 2007 году Slocum принял решение выделить проект в самостоятельную библиотеку под названием Ext JS. Это была самодостаточная система, которая могла использовать YUI лишь как опциональную базу, а со временем полностью от неё отказалась. Версии 1.x и 2.x закрепили репутацию Ext JS как библиотеки «для серьёзных приложений» — в отличие от jQuery, ориентированной на легковесные интерактивные страницы, Ext JS изначально позиционировался как инструмент для прикладного ПО в браузере.

Переломный момент произошёл в 2010 году, когда компания Jazva, владевшая Ext JS, объединилась с Ext JS Labs (где работал Слокам) и Sencha Touch (проектом для мобильной разработки) в единую компанию — Sencha Inc. Под этим брендом началась новая эпоха: Ext JS стал частью единой экосистемы, включающей инструменты разработки, системы сборки, облачные сервисы и коммерческую поддержку. Версии 3.x и 4.x (2010–2012) принесли масштабную реорганизацию архитектуры: переход от прототипного наследования к собственной системе классов с поддержкой миксинов, введение модели MVC, полную переписку сетки и систему темизации через Sass.

Начиная с Ext JS 5 (2014), фреймворк получил поддержку единой кодовой базы для десктопа и мобильных устройств, а в Ext JS 6 (2015) эта конвергенция была доведена до логического завершения: одна и та же модель, один и тот же контроллер, один роутер — но разные представления в зависимости от платформы (классическая десктопная тема Neptune и мобильная тема Triton).

В 2020 году Sencha была приобретена компанией Idera, что повлекло за собой изменение лицензионной политики и замедление темпов открытой разработки. Однако сообщество осталось активным, и с 2021 года разработка Ext JS частично продолжается в рамках проекта ExtWebComponents и открытых форков (например, ExtAngular, ExtReact — адаптации Ext-компонентов под современные фреймворки через Web Components). При этом классический Ext JS продолжает поддерживаться и используется в тысячах корпоративных систем по всему миру.


Архитектурные основы

В основе Ext JS лежит компонентная модель, где каждый элемент интерфейса является экземпляром класса Ext.Component или его наследника. Компонент — это минимальная единица, обладающая собственным DOM-представлением, состоянием, поведением и жизненным циклом. Простейший компонент может быть просто надписью (Ext.Component с html: 'Привет'), а сложнейший — многооконным приложением с вложенными панелями, сетками, инструментами и внутренней маршрутизацией.

Компоненты организуются в иерархию по принципу «контейнер–потомок». Класс Ext.Container (и его потомки — Panel, Window, Viewport, TabPanel и др.) может содержать другие компоненты. При добавлении компонента в контейнер автоматически строится дерево владения: контейнер становится родителем, вложенный элемент — потомком. Эта структура управляет визуальным расположением и жизненным циклом: при уничтожении контейнера все его потомки уничтожаются рекурсивно, что исключает утечки памяти.

Ключевым элементом управления компоновкой является система макетов (layouts). Макет определяет как и где будут размещены дочерние компоненты внутри контейнера. В отличие от CSS Flexbox или Grid, макеты Ext JS — это активные JavaScript-объекты, которые пересчитывают размеры и позиции в ответ на события: изменение размера окна, программное скрытие/показ элемента, обновление данных. Существуют как простые макеты (hbox, vbox, fit, card), так и сложные (column, table, anchor, border). Например, макет border позволяет строить классические трёхпанельные интерфейсы (север-центр-юг, запад-центр-восток) без единой строки CSS.

Каждый компонент получает уникальный itemId, иерархический id, и может быть найден в дереве через up(), down(), query(). Это обеспечивает устойчивую навигацию по структуре даже при динамическом изменении содержимого — критически важно для больших приложений.


Архитектурные паттерны

Начиная с версии 4, Ext JS официально поддерживает два основных подхода к организации кода: MVC (Model–View–Controller) и MVVM (Model–View–ViewModel). Оба паттерна внедрены как встроенные, строго типизированные механизмы фреймворка, с обязательными соглашениями о именовании, автоматической загрузкой классов и управлением связями.

В MVC приложение разделяется на три слоя.
Модель (Ext.data.Model) описывает структуру данных: поля, их типы, валидации, зависимости.
Представление (view) — это иерархия компонентов, обычно объявленная как класс, наследующий от Ext.Component или Ext.Container. Представление не содержит бизнес-логики и не обращается напрямую к данным.
Контроллер (Ext.app.Controller) связывает события представления (например, клик по кнопке) с действиями над моделью и хранилищем, а также реагирует на изменения данных, инициируя обновление интерфейса. Контроллер не управляет DOM — он управляет состоянием и потоком событий.

Связка между представлением и контроллером обеспечивается через селекторы — синтаксис, похожий на CSS-селекторы, но работающий с itemId, xtype, иерархией компонентов. Например, запись 'button[action=save]' означает: «найти все кнопки с action: 'save' внутри данного представления и подписаться на их событие click». Это позволяет избежать жёстких ссылок и обеспечивает гибкость при рефакторинге.

В MVVM роль посредника между представлением и моделью играет ViewModel (Ext.app.ViewModel). Это автономный объект, содержащий локальное состояние представления, вычисляемые поля и привязки (bindings). ViewModel не знает о контроллере и не связан с жизненным циклом представления напрямую — он существует параллельно и уничтожается вместе с представлением. Его главная функция — обеспечить реактивность без императивного кода.

Ключевой механизм MVVM — двусторонняя привязка данных (two-way binding). Когда разработчик объявляет привязку между полем firstName в ViewModel и значением textfield, то любое изменение в текстовом поле автоматически обновляет firstName, а любое программное изменение firstName — обновляет текстовое поле. Для сложных случаев поддерживаются формулы — функции, зависящие от других полей и автоматически пересчитываемые при их изменении (например, fullName = firstName + ' ' + lastName). Важно: формулы не являются выражениями в коде — они декларативно описываются в конфигурации ViewModel и управляются фреймворком, что исключает побочные эффекты и циклические зависимости.

И MVC, и MVVM могут сосуществовать в одном приложении: крупные разделы строятся по MVVM (например, форма редактирования), а глобальная навигация, аутентификация, интеграция с внешними API — управляются контроллерами. Это позволяет выбирать оптимальную модель в зависимости от задачи.


Работа с данными

В Ext JS данные отделены от интерфейса на архитектурном уровне. Даже простая таблица не содержит данных «в себе» — она лишь отображает содержимое хранилища (Ext.data.Store), которое, в свою очередь, управляет коллекцией экземпляров модели (Ext.data.Model).

Модель — это не просто POJO-объект. Это полноценный класс с строгим описанием структуры: каждое поле декларируется с типом (string, int, date, boolean, auto), при необходимости — с валидатором (presence, length, format, кастомная функция), конвертером (convert) и умолчательным значением (defaultValue). Модель поддерживает ассоциации: hasMany, belongsTo, hasOne, что позволяет строить графы данных без ручного сшивания объектов.

Хранилище выступает в роли буфера данных и координатора операций. Оно отвечает за:
— загрузку записей из источника (через прокси),
— локальное кэширование и сортировку,
— отслеживание изменений («грязных» записей),
— пакетную синхронизацию с сервером (sync()),
— пагинацию и фильтрацию на клиенте или сервере.

Ключевая особенность: хранилище не просто «хранит» данные — оно управляет их состоянием. Запись может находиться в одном из режимов: NEW, EDIT, COMMIT, REJECT. При вызове record.set('field', value) хранилище автоматически помечает запись как изменённую и добавляет её в очередь на синхронизацию. Это позволяет реализовать сложные сценарии — например, отмену всех изменений (rejectChanges()) или выборочную отправку (getModifiedRecords()).

Загрузка и сохранение данных осуществляются через прокси (Ext.data.proxy.Proxy). Прокси абстрагируют способ взаимодействия с источником:
Ajax — HTTP-запросы (GET/POST/PUT/DELETE),
Rest — строгая REST-семантика,
Memory — данные только в памяти (для тестов или временных коллекций),
LocalStorage и SessionStorage — постоянство на клиенте,
Direct — интеграция с серверными RPC-методами (например, через Ext.Direct в связке с Java или .NET).

Каждый прокси может включать ридер (reader) и писатель (writer) — классы, отвечающие за десериализацию ответа сервера и сериализацию запроса. Поддерживаются JSON, XML, массивы. При необходимости можно реализовать кастомный ридер для нестандартных форматов (например, для ответов старых SOAP-сервисов).

Важно: все операции с данными асинхронны по умолчанию, но фреймворк обеспечивает согласованность состояния даже при параллельных запросах. Например, если происходит редактирование записи во время загрузки новой страницы, хранилище не потеряет изменения — они сохраняются в буфере до завершения операции.


Инструменты разработки

Для поддержки жизненного цикла проекта Sencha предоставила два ключевых инструмента: Sencha Cmd и Sencha Architect.

Sencha Cmd — это консольная утилита, сочетающая функции сборщика, менеджера зависимостей, генератора кода и сервера разработки. Она использует Apache Ant под капотом, но скрывает сложность через высокоуровневые команды:
sencha app init — инициализация структуры проекта,
sencha app build — сборка production-версии с минификацией, объединением файлов, tree-shaking (в поздних версиях), внедрением темы,
sencha app watch — режим разработки с хот-релоадом и инкрементальной пересборкой,
sencha generate model/view/controller — шаблонное создание классов с соблюдением соглашений об именовании.

Особое внимание уделено темизации. Тема в Ext JS — это проект на Sass с параметризацией: цвета, отступы, радиусы скруглений, шрифты вынесены в переменные. Sencha Cmd компилирует Sass в CSS с учётом иерархии наследования тем и оптимизацией для конкретного набора используемых компонентов (так называемая theme slice), что позволяет сократить размер результирующего CSS на 60–80 % по сравнению с полной сборкой.

Sencha Architect — визуальный IDE-инструмент (на базе Electron), позволяющий проектировать интерфейс перетаскиванием компонентов, настраивать их свойства в панели инспектора, привязывать данные, описывать события — всё это с генерацией валидного Ext JS-кода. Architect не заменяет ручное кодирование, но значительно ускоряет создание макетов, форм и прототипов, особенно при работе с непрограммистами (аналитиками, дизайнерами). Код, сгенерированный Architect, полностью совместим с ручной разработкой и может быть доработан в любом редакторе.

Оба инструмента интегрируются с системами контроля версий: проект Sencha — это обычная файловая структура без бинарных артефактов (за исключением app.json, который текстовый), что позволяет использовать Git, SVN и т.п. без ограничений.


Установка и лицензирование

Ext JS исторически состоял из двух частей: Ext Core и Ext JS (базовая библиотека + компоненты).

Ext Core — это легковесный ядро (около 30 КБ минифицированного кода), предоставляющее кроссбраузерные абстракции для работы с DOM, событиями, Ajax, утилитами (Ext.each, Ext.merge, Ext.isString и т.д.). Он распространялся по лицензии MIT и мог свободно использоваться даже в коммерческих проектах без упоминания Sencha. Однако с выходом Ext JS 4 (2011) Ext Core был полностью интегрирован в основную библиотеку, и отдельного релиза больше не выпускалось.

Сам Ext JS с версии 2.0 (2008) перешёл на двойную лицензию:
GNU General Public License, версия 3 (GPL v3) — для open-source проектов, распространяемых также под GPL v3. Это означает, что приложение, использующее Ext JS по GPL, должно быть открытым, и его исходный код — доступен пользователям.
Коммерческая лицензия — для закрытых, проприетарных приложений. Лицензия привязана к разработчику или организации, включает право на техническую поддержку, обновления и использование в SaaS-продуктах без раскрытия исходного кода.

Такой подход типичен для многих enterprise-библиотек (например, Qt, iText). Он позволяет бесплатно использовать фреймворк в некоммерческой или open-source разработке, но требует оплаты при коммерческом внедрении. Важно: просто использование Ext JS в веб-приложении, даже если оно доступно в интернете, уже считается распространением — и если код закрыт, требуется коммерческая лицензия.

С 2021 года, после перехода под управление Idera, политика ужесточилась: бесплатная GPL-версия больше не публикуется на общедоступных CDN (вроде npm без авторизации), а доступ к репозиторию npm требует регистрации и принятия лицензионного соглашения. Это не отменяет право использовать уже загруженные GPL-версии, но затрудняет старт новых open-source проектов.

Установка сегодня осуществляется преимущественно через npm, но с обязательной аутентификацией в приватном репозитории Sencha (даже для GPL-сборок):

npm login --registry=https://npm.sencha.com --scope=@sencha
npm install @sencha/ext-web-components

Альтернативно — загрузка архива с сайта и ручная инициализация проекта через sencha app init --ext <путь>.


Пример: декларативное описание компонента и работа с данными

Рассмотрим типичный сценарий — редактируемая форма с привязкой к модели и сохранением на сервер.

Сначала определяется модель:

Ext.define('App.model.User', {
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'int' },
{ name: 'firstName', type: 'string', defaultValue: '' },
{ name: 'lastName', type: 'string', defaultValue: '' },
{ name: 'email', type: 'string',
validate: { type: 'email' } }
],
proxy: {
type: 'rest',
url: '/api/users',
reader: { type: 'json', rootProperty: 'data' }
}
});

Затем — представление с ViewModel:

Ext.define('App.view.user.EditForm', {
extend: 'Ext.form.Panel',
xtype: 'user-edit-form',

viewModel: {
type: 'user' // ссылка на App.view.user.UserViewModel
},

items: [{
xtype: 'textfield',
fieldLabel: 'Имя',
bind: '{record.firstName}'
}, {
xtype: 'textfield',
fieldLabel: 'Фамилия',
bind: '{record.lastName}'
}, {
xtype: 'textfield',
fieldLabel: 'Email',
vtype: 'email',
bind: '{record.email}'
}],

buttons: [{
text: 'Сохранить',
handler: 'onSave'
}]
});

ViewModel обеспечивает реактивность:

Ext.define('App.view.user.UserViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.user',

data: {
record: null // будет заполнено контроллером
},

formulas: {
fullName: function(get) {
return (get('record.firstName') || '') + ' ' +
(get('record.lastName') || '');
}
}
});

Контроллер связывает всё вместе:

Ext.define('App.controller.User', {
extend: 'Ext.app.Controller',

init: function() {
this.listen({
component: {
'user-edit-form button[text=Сохранить]': {
click: 'onSave'
}
}
});
},

onSave: function(button) {
var form = button.up('form'),
viewModel = form.getViewModel(),
record = viewModel.get('record');

form.updateRecord(record); // переносит значения из полей в модель

if (record.isValid()) {
record.save({
success: function() {
Ext.toast('Данные сохранены');
},
failure: function() {
Ext.Msg.alert('Ошибка', 'Не удалось сохранить');
}
});
} else {
form.getForm().markInvalid(record.getValidation().errors);
}
}
});

Обратите внимание:
— Нет ручной работы с DOM (document.getElementById),
— Нет императивного копирования значений (textField.getValue()),
— Валидация делегирована модели,
— Сохранение — через save() модели, который использует прокси,
— Интерфейс реагирует на изменения автоматически благодаря bind.

Это и есть суть подхода Ext JS: описание вместо императивного управления.


Современный статус и альтернативы

На 2025 год Ext JS остаётся активно поддерживаемым продуктом, но его роль в новых проектах изменилась. Он редко выбирается для стартапов или consumer-приложений, где важна скорость итераций и экосистема open-source. Однако в корпоративном секторе — особенно в финансах, государственном управлении, промышленной автоматизации — он по-прежнему востребован там, где:
— требуется поддержка legacy-браузеров (IE11 в госсекторе),
— нужны готовые enterprise-компоненты «из коробки» (сетки с экспортами, планировщики, карты, отчёты),
— критична предсказуемость и сопровождаемость в течение 10+ лет,
— интеграция с Java/.NET-бэкендами через Ext.Direct или SOAP.

Sencha продолжает выпускать обновления (последняя LTS-версия — Ext JS 7.6, 2023 г.), добавляя поддержку TypeScript, улучшая производительность рендеринга, интегрируя Web Components. Проект ExtWebComponents позволяет использовать отдельные компоненты Ext JS (например, ext-grid, ext-chart) в любом современном фреймворке как стандартные HTML-теги — это гибридный путь миграции.

Альтернативы условно делятся на:
Библиотеки компонентов: AG Grid, Kendo UI, Syncfusion — предлагают схожие по функциональности сетки и контролы, но без единой архитектуры приложения.
Фреймворки с enterprise-библиотеками: Angular + DevExtreme, React + Material-UI + кастомная логика — гибче, но требуют больше усилий на интеграцию и тестирование.
Low-code платформы: ELMA365, Creatio, OutSystems — уходят от ручного кодирования вообще, но жертвуют гибкостью.

Выбор Ext JS сегодня — это расчёт на долгосрочную стабильность, минимальное время вывода MVP и соответствие регуляторным требованиям. В этом смысле он остаётся уникальным решением в своём классе.


Отладка Ext JS в инструментах разработчика

Одно из сильных преимуществ Ext JS — высокая интроспектируемость приложения во время выполнения. Фреймворк не скрывает внутреннее состояние: все объекты доступны из консоли браузера, имеют читаемые имена классов, иерархию владения, ссылки на конфигурации и данные. Это создаёт мощную основу для диагностического анализа без необходимости в специализированных плагинах (в отличие, например, от React DevTools, где требуется отдельное расширение).

Ключевой инструмент — глобальный объект Ext, предоставляющий доступ к реестру компонентов, классов, видмоделей и утилитам. Начать отладку можно с запроса к Ext.ComponentQuery, аналогичного CSS-селекторам, но работающему на уровне объектной модели:

// Найти все панели на странице
Ext.ComponentQuery.query('panel');

// Найти компонент по itemId (локальному, уникальным в пределах контейнера)
Ext.ComponentQuery.query('#myButton');

// Найти активное окно верхнего уровня
Ext.ComponentQuery.query('window[active=true]')[0];

// Найти грид с указанным xtype и получить его хранилище
var grid = Ext.ComponentQuery.query('myapp-grid')[0];
var store = grid.getStore();
store.getData().items; // массив записей

Каждый компонент, хранилище, модель имеет метод .toJSON() или .get(), возвращающий плоскую структуру текущего состояния — это особенно полезно при проверке значений полей или изменений в записи:

// Получить текущее значение из текстового поля
var field = Ext.ComponentQuery.query('#emailField')[0];
field.getValue(); // строка

// Или через модель, привязанную к ViewModel
var vm = field.up('form').getViewModel();
vm.get('record.email'); // то же значение, но из модели

// Посмотреть все «грязные» поля записи
var record = vm.get('record');
record.getChanges(); // { firstName: 'Новое', email: 'new@ex.ample' }

Жизненное дерево приложения можно визуализировать через владельческие ссылки. У любого компонента есть свойства:

  • ownerCt — непосредственный родительский контейнер,
  • up() — метод для подъёма по иерархии (component.up('form') найдёт ближайшую форму выше),
  • down() — спуск вниз по дереву (form.down('#submitBtn')),
  • floatingItems, menu, toolbars — ссылки на вспомогательные элементы.

При возникновении ошибки в обработчике события (например, TypeError: Cannot read property 'save' of undefined) достаточно:

  1. Поставить breakpoint в обработчике (через debugger; или в Sources),
  2. В консоли вызвать this — он укажет на экземпляр контроллера или компонента, откуда вызван метод,
  3. Использовать arguments или замыкание, чтобы уточнить контекст вызова.

Важно: в production-сборках Ext JS по умолчанию включается minification, что «схлопывает» имена методов и свойств. Для отладки рекомендуется использовать development-сборку (ext-all-debug.js или сборка через sencha app build development), где сохранились имена классов, параметров и комментарии.

Для диагностики проблем с загрузкой данных полезно следить за сетевыми запросами (вкладка Network), но не только за HTTP-статусом — Ext JS добавляет к ответу сервера заголовок X-Sencha-Trace, а в консоли можно включить логирование прокси:

Ext.data.Connection.prototype.log = true;
// или на уровне прокси модели
proxy: {
type: 'ajax',
url: '/api/data',
listeners: {
exception: function(proxy, response) {
console.error('Ошибка загрузки:', response.responseText);
}
}
}

Таким образом, отладка в Ext JS — это целенаправленный осмотр состояния через открытую, документированную объектную модель. Это снижает порог входа для новых разработчиков в уже существующий проект и повышает предсказуемость поведения системы.


Миксины

В отличие от классического наследования («is-a»), где класс расширяет другой класс, Ext JS активно использует миксины (mixins) — механизм горизонтального внедрения функциональности («has-a»). Миксин — это автономный класс, содержащий методы, свойства и обработчики событий, которые могут быть добавлены в любой другой класс без изменения его иерархии.

Синтаксис объявления миксина:

Ext.define('App.mixin.DirtyTracking', {
extend: 'Ext.Mixin',

mixinConfig: {
id: 'dirtytracking'
},

// Добавляются методы и обработчики
onFieldChange: function(field, newValue, oldValue) {
if (newValue !== oldValue) {
this.markAsDirty();
}
},

markAsDirty: function() {
this.dirty = true;
this.fireEvent('dirtychange', this, true);
}
});

Подключение к компоненту:

Ext.define('App.view.FormPanel', {
extend: 'Ext.form.Panel',
mixins: ['App.mixin.DirtyTracking'],

initComponent: function() {
this.callParent();
// Подписка на события полей — внутри миксина или здесь
this.on('fieldchange', this.onFieldChange, this);
}
});

Преимущества миксинов:

  • Повторное использование: один и тот же миксин можно подключить к форме, сетке, диалоговому окну — любому компоненту, нуждающемуся в трекинге изменений.
  • Разделение ответственности: логика валидации, логирования, доступности, локализации выносится в отдельные миксины, а не размазывается по базовым классам.
  • Избежание ромбовидного наследования: когда два базовых класса предоставляют одну и ту же функциональность (например, Draggable и Resizable), миксины позволяют комбинировать их без конфликтов.

Ext JS поставляется с рядом встроенных миксинов:

  • Ext.util.Observable — добавляет систему событий (уже включён в Ext.Base, так что явно не требуется),
  • Ext.mixin.Bindable — поддержка привязок (bindings),
  • Ext.mixin.Selectable — поведение выбора (для гридов, деревьев),
  • Ext.mixin.Factoryable — фабричный паттерн для динамического создания компонентов.

Миксины уважают жизненный цикл компонента: если миксин содержит метод initComponent, он будет вызван после initComponent основного класса, но до afterRender. Это позволяет безопасно инициализировать состояние, не нарушая порядка событий.

Микшины — не «патчинг» прототипа. Они внедряются на этапе определения класса, и каждый экземпляр получает свою копию методов (в пределах ограничений JavaScript), что исключает побочные эффекты при совместном использовании.


Ext JS в контексте low-code платформ

Платформы класса Creatio (ранее bpm’online), ELMA365 и аналогичные enterprise-решения для автоматизации бизнес-процессов часто используют Ext JS как базовую front-end-платформу для построения пользовательских интерфейсов — особенно в модулях, требующих высокой интерактивности: CRM-панели, конструкторы форм, аналитические дашборды, редакторы процессов.

Важно подчеркнуть: в таких системах Ext JS не используется «в чистом виде». Он интегрируется в собственную метасистему:

  • Вместо ручного объявления Ext.define('MyApp.view...') интерфейсы описываются в метаданных (XML или JSON-конфигурации), которые в runtime компилируются во внутренние объекты Ext JS.
  • Классы компонентов заменяются модулями страниц (например, BasePageV2, SectionPageV2 в Creatio), которые наследуют от абстракций платформы, а не напрямую от Ext.panel.Panel.
  • Привязка данных идёт не к Ext.data.Model, а к сущностям доменной модели (например, Contact, Account), управляемым серверным API и клиентским кэшем.
  • Механизм событий расширяется платформо-зависимыми триггерами («до сохранения», «после загрузки», «при смене статуса»), которые транслируются в вызовы Ext-событий.

Тем не менее, знание Ext JS даёт разработчику критическое преимущество при кастомизации таких платформ:

  • Понимание иерархии компонентов позволяет точно находить целевые элементы через ComponentQuery.
  • Опыт работы с ViewModel и bindings ускоряет создание реактивных полей и вычисляемых значений.
  • Знание жизненного цикла (init, render, destroy) помогает корректно внедрять логику без утечек памяти.
  • Умение отлаживать через консоль позволяет быстро локализовать проблему в кастомном коде, не дожидаясь полной пересборки метапакета.

Например, в Creatio страница контакта — это экземпляр класса, унаследованного от BasePageV2, который внутри содержит Ext.container.Container с вкладками, формами, гридами. При переопределении метода init разработчик может добавить кнопку:

init: function() {
this.callParent(arguments);
this.addButton({
caption: "Проверить контрагента",
tag: "checkContragent",
click: { bindTo: "onCheckContragentClick" }
});
}

Здесь addButton — метод платформы, но он создаёт стандартный Ext.button.Button, подписанный на событие через механизм, совместимый с handler в Ext JS.

Таким образом, Ext JS здесь выступает как движок отрисовки и поведения, а платформа — как надстройка метамодели, безопасности, интеграций и управления жизненным циклом. Знание ядра позволяет эффективно работать с надстройкой, используя заложенные расширения.